/*
 *    Copyright (c) 2018-2025, lengleng All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * Redistributions of source code must retain the above copyright notice,
 * this list of conditions and the following disclaimer.
 * Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution.
 * Neither the name of the pig4cloud.com developer nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 * Author: lengleng (wangiegie@gmail.com)
 */
package com.pig4cloud.pigx.user.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.pig4cloud.pigx.common.core.constant.CommonConstants;
import com.pig4cloud.pigx.common.core.constant.SecurityConstants;
import com.pig4cloud.pigx.common.core.util.R;
import com.pig4cloud.pigx.common.customize.api.exception.OpenApiException;
import com.pig4cloud.pigx.common.customize.enums.CommonDictEnum;
import com.pig4cloud.pigx.common.customize.i18n.util.MessageUtils;
import com.pig4cloud.pigx.common.customize.mysql.utils.MysqlUtil;
import com.pig4cloud.pigx.common.customize.utils.id.IdWorker;
import com.pig4cloud.pigx.system.constant.EmailTemplateTypeEnum;
import com.pig4cloud.pigx.user.api.feign.RippleRemoteAuthService;
import com.pig4cloud.pigx.user.api.feign.RippleRemoteGatewayService;
import com.pig4cloud.pigx.user.api.feign.RippleRemoteUpmsService;
import com.pig4cloud.pigx.user.api.feign.RippleRemoteWalletService;
import com.pig4cloud.pigx.user.constant.PublicChainTypeEnums;
import com.pig4cloud.pigx.user.constant.RedisMessageType;
import com.pig4cloud.pigx.user.dto.UserMobileDTO;
import com.pig4cloud.pigx.user.entity.SysUser;
import com.pig4cloud.pigx.user.entity.User;
import com.pig4cloud.pigx.user.enums.UserApiKeyEnum;
import com.pig4cloud.pigx.user.mapper.UserMapper;
import com.pig4cloud.pigx.user.mapstruct.UserMobileStruct;
import com.pig4cloud.pigx.user.mapstruct.UserStruct;
import com.pig4cloud.pigx.user.service.EmailService;
import com.pig4cloud.pigx.user.service.UserService;
import com.pig4cloud.pigx.user.util.StringRandom;
import com.pig4cloud.pigx.user.util.TronVerifyMessage;
import com.pig4cloud.pigx.user.util.UserUtil;
import com.pig4cloud.pigx.user.util.Web3jSignatureValid;
import com.pig4cloud.pigx.user.vo.UserApiKeyCacheVO;
import com.pig4cloud.pigx.user.vo.UserPageApiKeyVO;
import lombok.RequiredArgsConstructor;
import org.apache.logging.log4j.util.Strings;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.http.HttpHeaders;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;

import static com.pig4cloud.pigx.user.constant.I18nMessages.CHANGE_FAILURE;
import static com.pig4cloud.pigx.user.constant.I18nMessages.EMAIL_CANNOT_BE_EMPTY;
import static com.pig4cloud.pigx.user.constant.I18nMessages.EMAIL_HAS_BEEN_REGISTERED;
import static com.pig4cloud.pigx.user.constant.I18nMessages.INVALID_INVITATION_CODE;
import static com.pig4cloud.pigx.user.constant.I18nMessages.INVITER_IS_DISABLED;
import static com.pig4cloud.pigx.user.constant.I18nMessages.MODIFY_SUCCESSFULLY;
import static com.pig4cloud.pigx.user.constant.I18nMessages.PASSWORD_CANNOT_BE_EMPTY;
import static com.pig4cloud.pigx.user.constant.I18nMessages.PLEASE_ENTER_THE_CORRECT_PUBLIC_CHAIN;
import static com.pig4cloud.pigx.user.constant.I18nMessages.REQUEST_PARAMETER_INVALID;
import static com.pig4cloud.pigx.user.constant.I18nMessages.SIGNATURE_VERIFICATION_ERROR;
import static com.pig4cloud.pigx.user.constant.I18nMessages.USER_DOES_NOT_EXIST;
import static com.pig4cloud.pigx.user.constant.I18nMessages.USER_LANGUAGE_CANNOT_BE_EMPTY;
import static com.pig4cloud.pigx.user.constant.I18nMessages.USER_REGISTRATION_SUCCEEDED;
import static com.pig4cloud.pigx.user.constant.I18nMessages.USER_UID_CANNOT_BE_EMPTY;
import static com.pig4cloud.pigx.user.constant.I18nMessages.USER_WALLET_ADDRESS_NOT_AUTHORIZED;
import static com.pig4cloud.pigx.user.constant.I18nMessages.VERIFICATION_CODE_IS_INCALID;

/**
 * 平台用户
 *
 * @author pigx code generator
 * @date 2022-08-08 15:07:46
 */
@Service
@RequiredArgsConstructor
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {

    /**
     * RedisKeyEnum
     */
    private final StringRedisTemplate stringRedisTemplate;
    /**
     * 雪花id
     */
    private final IdWorker idWorker;
    /**
     * pigx-gateway的Feign
     */
    private final RippleRemoteGatewayService rippleRemoteGatewayService;
    /**
     * pigx-upms的Feign
     */
    private final RippleRemoteUpmsService rippleRemoteUpmsService;
    /**
     * pigx-auth的Feign
     */
    private final RippleRemoteAuthService rippleRemoteAuthService;
    /**
     * ripple-wallet-biz的Feign
     */
    private final RippleRemoteWalletService remoteWalletService;
    /**
     * ripple-agent-biz的Feign
     */
    private final RippleRemoteUpmsService rooRemoteAgentService;

    /**
     * 邮箱邮件发送
     */
    private final EmailService emailService;

    private final RedisTemplate redis;
    private final UserUtil userUtil;


    /**
     * 用户注册
     *
     * @param userMobileDTO
     * @return
     */
    @Override
    public R userRegister(UserMobileDTO userMobileDTO) {
        User user = UserMobileStruct.INSTANCE.dto2user(userMobileDTO);
        if (user.getEmail().isEmpty()) {
            return R.failed(MessageUtils.get(EMAIL_CANNOT_BE_EMPTY));
        }
        User userInfo = this.getOne(new QueryWrapper<User>().eq("email", user.getEmail()));
        if (userInfo != null) {
            return R.failed(MessageUtils.get(EMAIL_HAS_BEEN_REGISTERED));
        }
        //用户邮箱验证码验证
        /*String emailVerificationCode = stringRedisTemplate.opsForValue()
                .get(user.getEmail() + " massage = " + RedisMessageType.Customer_Registration);
        if (user.getUserVerificationCode() == null || emailVerificationCode == null ||
                !user.getUserVerificationCode().equals(emailVerificationCode)) {
            return R.failed(MessageUtils.get(VERIFICATION_CODE_IS_INCALID));
        }*/
        //查询邀请码是否正确
        if (user.getInvitation() != null && !user.getInvitation().isEmpty()) {
            // 邀请人
            User inviter = this.getOne(new QueryWrapper<User>().eq("invite_code", user.getInvitation()));
            if (inviter == null) {
                // 邀请人不存在
                return R.failed(MessageUtils.get(INVALID_INVITATION_CODE));
            } else if (inviter.getStatus().equals(1)) {
                // 邀请人被禁用
                return R.failed(MessageUtils.get(INVITER_IS_DISABLED));
            }
            // 获取邀请链最上级用户
            User parent = getParent(inviter);
            user.setParentAgentId(parent.getUid());
            //用户邀请人uid
            user.setInviterId(inviter.getUid());
            // 经纪人给用户的默认返佣比例
            Object invitedUserProportion = redis.opsForValue().get(parent.getUid() + "_invited_user_proportion");
            if (invitedUserProportion != null) {
                user.setAgentCommissionPercentage((Integer) invitedUserProportion);
            }
        }
        //查询注册用的的信息
        User userInfoByAddress = this.getOne(new QueryWrapper<User>().select("id", "uid").eq("address", user.getAddress()));
        if (userInfoByAddress == null) {
            return R.failed(MessageUtils.get(USER_WALLET_ADDRESS_NOT_AUTHORIZED));
        }
        user.setId(userInfoByAddress.getId());
        //用户邀请码
        user.setInviteCode(StringRandom.getRandomInviteCode(userInfoByAddress.getUid()));
        //保存用户信息
        this.updateById(user);
        //钱包模块 注册的用户
        remoteWalletService.create(userInfoByAddress.getUid(), SecurityConstants.FROM_IN);
        //删除缓存验证码
        stringRedisTemplate.delete(user.getEmail() + " massage = " + RedisMessageType.Customer_Registration);
        return R.ok(MessageUtils.get(USER_REGISTRATION_SUCCEEDED));
    }

    /**
     * 密码解密后请求pigx-gateway中
     *
     * @param header
     * @param userMobileDTO
     * @return
     */
    @Override
    public R login(HttpHeaders header, UserMobileDTO userMobileDTO) {
        User user = UserMobileStruct.INSTANCE.dto2user(userMobileDTO);
        if (user.getAddress() == null || user.getAddress() == "" ||
                user.getSignature() == null || user.getSignature() == "" ||
                user.getChain() == null || user.getChain() == "") {
            return R.failed(MessageUtils.get(REQUEST_PARAMETER_INVALID));
        }
        //获取加签客户massage，判断是否存在
        String addressMessage = stringRedisTemplate.opsForValue()
                .get(user.getAddress() + " massage = " + RedisMessageType.Customer_Address_Message);
        if (addressMessage == null || addressMessage.isEmpty()) {
            return R.failed(MessageUtils.get(USER_WALLET_ADDRESS_NOT_AUTHORIZED));
        }
        //判断用户登录公链
        if (user.getChain() != null && !user.getChain().isEmpty() &&
                user.getChain().equals(PublicChainTypeEnums.ETH.getCode())) {
            //以太坊地址登录时
            //校验加密后密文
            boolean signatureValid = Web3jSignatureValid.isSignatureValid(
                    user.getAddress(), user.getSignature(), addressMessage);
            //校验正确请求pigx-gatewey登录生成token返回
            if (!signatureValid) {
                return R.failed(MessageUtils.get(SIGNATURE_VERIFICATION_ERROR));
            }
        } else if (user.getChain() != null && !user.getChain().isEmpty() &&
                user.getChain().equals(PublicChainTypeEnums.TRX.getCode())) {
            //波场
            boolean verify = TronVerifyMessage.verify(addressMessage, user.getSignature(), user.getAddress());
            if (!verify) {
                return R.failed(MessageUtils.get(SIGNATURE_VERIFICATION_ERROR));
            }
        } else {
            return R.failed(MessageUtils.get(PLEASE_ENTER_THE_CORRECT_PUBLIC_CHAIN));
        }
        //根据地址查询用户
        User userInfo = this.getOne(new QueryWrapper<User>().eq("address", user.getAddress()));
        //保存用户地址
        if (userInfo == null) {
            //系统id
            user.setId(null);
            //是否经纪人
            user.setIsBroker(0);
            //是否确认风险告知
            user.setIsConfrimRisk(0);
            //账户状态
            user.setStatus(0);
            //雪花id
            user.setUid(idWorker.nextId());
            //用户语言设置
            user.setLanguageCode("zh_CN");
            this.save(user);
        }

        try {
            //请求oauth2登录接口
            Map<String, Object> param = new HashMap<>();
            param.put("username", user.getAddress());
            param.put("password", user.getAddress());
            param.put("scope", "server");
            param.put("grant_type", "password");
            Map<String, Object> map = rippleRemoteGatewayService.sendGateWay(header, param, SecurityConstants.FROM_IN);
            //返回用户是否注册
            if (userInfo == null || userInfo.getEmail() == null || userInfo.getEmail().isEmpty()) {
                //不存在则注册
                map.put("userIsExist", false);
                //返回用户信息
                map.put("id", user.getId());
                map.put("email", user.getEmail());
                map.put("address", user.getAddress());
                map.put("uid", user.getUid());
            } else {
                //存在则跳过注册
                map.put("userIsExist", true);
                //返回用户信息
                userInfo.setMoneyPassword(null);
                map.put("id", userInfo.getId());
                map.put("email", userInfo.getEmail());
                map.put("address", userInfo.getAddress());
                map.put("uid", userInfo.getUid());
            }
            //删除缓存签名原文
            stringRedisTemplate.delete(user.getAddress() + " massage = " + RedisMessageType.Customer_Address_Message);
            return R.ok(map);
        } catch (Exception e) {
            //登录返回异常信息格式化
            e.printStackTrace();
            if (e.getMessage() != null) {
                String detailMessage = e.getMessage();
                String message = detailMessage.substring(detailMessage.indexOf(": [{") + 3)
                        .substring(0, detailMessage.substring(detailMessage.indexOf(": [{") + 3).length() - 1);
                JSONObject jsonObject = JSON.parseObject(message);
                return R.failed(jsonObject);
            }
            return R.failed(e);
        }
    }

    /**
     * 用户修改邮箱
     */
    @Override
    public R modifyEmail(UserMobileDTO userMobileDTO) {
        User user = UserMobileStruct.INSTANCE.dto2user(userMobileDTO);

        if (user.getEmail() == null || user.getEmail().isEmpty()) {
            return R.failed(MessageUtils.get(EMAIL_CANNOT_BE_EMPTY));
        }
        //用户邮箱验证码验证
        String emailVerificationCode = stringRedisTemplate.opsForValue()
                .get(user.getEmail() + " massage = " + RedisMessageType.Customer_Modify_Email);
        if (user.getUserVerificationCode() == null || emailVerificationCode == null ||
                !user.getUserVerificationCode().equals(emailVerificationCode)) {
            return R.failed(MessageUtils.get(VERIFICATION_CODE_IS_INCALID));
        }
        //修改用户邮箱
        User updateUser = new User();
        updateUser.setEmail(user.getEmail());
        boolean b = this.update(updateUser, new QueryWrapper<User>().eq("uid", user.getUid()));
        if (b) {
            //删除缓存验证码
            stringRedisTemplate.delete(user.getEmail() + " massage = " + RedisMessageType.Customer_Modify_Email);
            return R.ok(MessageUtils.get(MODIFY_SUCCESSFULLY));
        }
        return R.failed(MessageUtils.get(CHANGE_FAILURE));
    }

    /**
     * 用户修改交易密码
     */
    @Override
    public R modifyMoneyPassword(UserMobileDTO userMobileDTO) {
        User user = UserMobileStruct.INSTANCE.dto2user(userMobileDTO);

        if (user.getNewMoneyPassword() == null || user.getNewMoneyPassword().isEmpty()) {
            return R.failed(MessageUtils.get(PASSWORD_CANNOT_BE_EMPTY));
        }
        //用户邮箱验证码验证
        String emailVerificationCode = stringRedisTemplate.opsForValue()
                .get(user.getEmail() + " massage = " + RedisMessageType.Customer_Modify_MONEY_PASSWORD);
        if (user.getUserVerificationCode() == null || emailVerificationCode == null ||
                !user.getUserVerificationCode().equals(emailVerificationCode)) {
            return R.failed(MessageUtils.get(INVALID_INVITATION_CODE));
        }

        User updateUser = new User();
        updateUser.setMoneyPassword(user.getNewMoneyPassword());
        boolean b = this.update(updateUser, new QueryWrapper<User>().eq("uid", user.getUid()));
        if (b) {
            //删除缓存验证码
            stringRedisTemplate.delete(user.getEmail() + " massage = " + RedisMessageType.Customer_Modify_MONEY_PASSWORD);
            return R.ok(MessageUtils.get(MODIFY_SUCCESSFULLY));
        }
        return R.failed(MessageUtils.get(CHANGE_FAILURE));
    }

    /**
     * 修改用户语言
     *
     * @param userMobileDTO
     * @return
     */
    @Override
    public R modifyingUserLanguage(UserMobileDTO userMobileDTO) {
        User user = UserMobileStruct.INSTANCE.dto2user(userMobileDTO);

        if (user.getUid() == null) {
            return R.failed(MessageUtils.get(USER_UID_CANNOT_BE_EMPTY));
        } else if (user.getLanguageCode() == null || user.getLanguageCode().isEmpty()) {
            return R.failed(MessageUtils.get(USER_LANGUAGE_CANNOT_BE_EMPTY));
        }
        User updateUser = new User();
        updateUser.setLanguageCode(user.getLanguageCode());
        boolean updateFlag = this.update(updateUser, new QueryWrapper<User>()
                .eq("uid", user.getUid()));
        if (updateFlag) {
            return R.ok(MessageUtils.get(MODIFY_SUCCESSFULLY));
        } else {
            return R.failed(MessageUtils.get(CHANGE_FAILURE));
        }
    }

    /**
     * 修改平台用户经纪人状态
     *
     * @param user 平台用户
     * @return R
     */
    @Override
    public R updateAgentById(User user) {
        Boolean userFlag;
        //查询用户是否存在
        User userInfo = this.getById(user);
        if (userInfo == null) {
            return R.failed(MessageUtils.get(USER_DOES_NOT_EXIST));
        }
        user.setUpdateTime(LocalDateTime.now());

        SysUser sysUser = new SysUser();
        // 将经纪人uid设置为系统用户主键
        sysUser.setUserId(userInfo.getUid());
        // 登录账号
        sysUser.setUsername(user.getUserName());
        if (user.getIsBroker().equals(1)) {
            // 登录密码
            sysUser.setPassword(new BCryptPasswordEncoder().encode(user.getPassword()));
            sysUser.setName(user.getUserName());
            // 邮箱
            sysUser.setEmail(user.getEmail());
            // 设置经纪人 部门Id
            sysUser.setDeptId(3L);
        }


        if (user.getIsBroker().equals(1)) {
            // 普通用户 -> 经纪人
            sysUser.setLockFlag(CommonConstants.STATUS_NORMAL);
            this.updateChildren(user.getUid(), user);
            // 平台给经纪人默认返佣比例
            Object agentUserProportion = redis.opsForValue().get("agent_user_proportion");
            if (agentUserProportion != null) {
                user.setPlatformCommissionPercentage((Integer) agentUserProportion);
            }
        } else {
            // 经济人 -> 普通用户 如果本次是取消经纪人身份操作，则冻结系统用户
            sysUser.setLockFlag(CommonConstants.STATUS_LOCK);
            this.updateChildren(this.getParent(user).getUid(), user);
        }
        // 更新系统用户
        R upmsR = rippleRemoteUpmsService.addUserByAgent(sysUser, SecurityConstants.FROM_IN);
        if (upmsR.getCode() != 0) {
            return upmsR;
        }
        userFlag = this.updateById(user);

        if (userFlag) {
            if (user.getIsBroker().equals(1)) {
                // 发送成为经纪人邮件
                emailService.sendEmail(user, EmailTemplateTypeEnum.BECOME_BROKER.getCode());
            }

            return upmsR;
        }
        return R.failed();
    }

    /**
     * 修改用户禁用状态
     *
     * @param user 平台用户
     * @return
     */
    @Override
    public R updateStatusById(User user) {
        if (user.getStatus().equals(1)) {
            //根据用户钱包地址删除用户token
            User userInfo = this.getById(user);
            rippleRemoteAuthService.removeTokenByUsername(userInfo.getAddress(), SecurityConstants.FROM_IN);
        }

        boolean b = this.updateById(user);
        if (b) {
            return R.ok();
        }
        return R.failed();

    }

    /**
     * 获取父级
     */
    private User getParent(User user) {
        // 如果不是经纪人并且拥有上级,无限内卷查询最上级经纪人
        if (user.getIsBroker().toString().equals(CommonDictEnum.YesOrNo.NO.getValue()) && user.getInviterId() != null) {
            User one = this.getOne(new QueryWrapper<User>().eq("uid", user.getInviterId()));
            if (one == null) {
                return user;
            }
            return this.getParent(one);
        } else {
            return user;
        }
    }

    /**
     * 晋升高管 成为经纪人的时候，将曾经自己邀请过的所有用户，拉到自己名下
     * 出门右拐 从经纪人变为普通用户时，将自己名下全链路邀请用户，全部还给自己的最高领导
     *
     * @param parentId 最高父级经纪人 id
     * @param user     用户
     */
    private void updateChildren(Long parentId, User user) {
        user.setParentAgentId(parentId);
        baseMapper.updateById(user);
        List<User> list = baseMapper.selectList(new QueryWrapper<User>() {{
            eq("inviter_id", user.getUid());
        }});
        if (list.size() > 0) {
            list.forEach(e -> {
                if (e != null) {
                    this.updateChildren(parentId, e);
                }
            });
        }
    }

    /**
     * 查询拥有apiKey的用户
     *
     * @param page      分页参数
     * @param condition 查询条件
     * @return {@link Page< UserPageApiKeyVO>}
     * @since 2022/10/28 16:25
     */
    @Override
    public Page<UserPageApiKeyVO> pageApiKey(Page<User> page, UserPageApiKeyVO condition) {
        QueryWrapper<User> query = new QueryWrapper<>();
        query.lambda()
                .select(User::getUid, User::getEmail, User::getApiKeyNum)
                .like(!Objects.isNull(condition.getUid()), User::getUid, condition.getUid())
                .like(Strings.isNotBlank(condition.getEmail()), User::getEmail, condition.getEmail())
                .gt(User::getApiKeyNum, 0)
                .orderByDesc(User::getApiKeyNum);

        Page<User> userPage = baseMapper.selectPage(page, query);
        // 转换为vo类
        List<UserPageApiKeyVO> voList = UserStruct.INSTANCE.list2PageApiKeyVOList(userPage.getRecords());
        return MysqlUtil.transferPage(userPage, voList);
    }

    /**
     * 根据AccessKey查询用户信息
     *
     * @param accessKey 访问密钥
     * @return {@link User}
     * @since 2022/11/7 18:18
     */
    @Override
    public User userInfoByAccessKey(String accessKey) {

        // 根据accessKey查询apiKey信息
        UserApiKeyCacheVO apiKey = userUtil.getApiKeyCache(accessKey);

        // 为空 || 封禁状态
        if (Objects.isNull(apiKey) || !Objects.equals(UserApiKeyEnum.Status.NORMAL.getValue(), apiKey.getStatus())) {
            // 无效的AccessKey
            throw new OpenApiException(20004, "Invalid API-key, IP, or permissions for action.");
        }

        // 获取AccessKey对应的用户信息
        QueryWrapper<User> query = new QueryWrapper<>();
        query.lambda()
                .eq(User::getUid, apiKey.getUserId());

        return baseMapper.selectOne(query);
    }
}
